package everything.search;

import java.io.File;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.TreeSet;

import everything.Automaton;
import everything.WorldMap;

public class ASearch {

	protected PriorityQueue<ASearchNode> _expansionTree;
	protected WorldMap _map;
	protected SearchAgentState _currState;
	protected SearchAgent _agent;
	protected Set<SearchAgentState> _oldStates;

	protected int T;

	protected static PrintWriter _out;

	static{
		try{
		File f = new File ("log.txt");
		f.delete();
		f.createNewFile();
		f.setWritable(true);
		f.setReadable(true);
		f.setExecutable(true);
		_out = new PrintWriter(f);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	protected ASearchNode _goal = null;
	// constructor
	// value is searchAgent.h result
	public ASearch(SearchAgentState state, WorldMap map, SearchAgent agent) {
		_map = map;
		_currState = state;
		_expansionTree = new PriorityQueue<ASearchNode>();
		_oldStates = new TreeSet<SearchAgentState>();
		_oldStates.add(_currState);
		// entering first node, starting point
		_expansionTree.add(new ASearchNode(_currState, null, null));
		_agent = agent;
	}

	public ASearchNode search(){
		while(_goal==null){
			expand();
		}
		_out.close();
		System.out.println("******************************");
		System.out.println("TREE FULLY EXPANDED WITH T="+T);
		System.out.println("******************************");
		return _goal;
	}

	//creates a new ASearchNode and adds to expansionTree
	protected void createNode(SearchAgentState state, ASearchNode parent, String direction){
		ASearchNode tmp = new ASearchNode(state, parent, direction);//, value);
		tmp.setValue(_agent);
		_out.print("Inserting State with value: [" + tmp.getValue() +","+ (tmp.getDepth()) +"]  :" + state + "... ");
		if(_oldStates.add(state)){
			_out.println("OK");
			_expansionTree.add(tmp);
		} else {
			_out.println("SIMILAR");
			//System.out.println("Similar - " + state);
		}
	}

	public boolean allFlagsTaken(SearchAgentState state){
		boolean flag = true;
		boolean[] flags = state.getFlags();
//		int[][] mapFlags = _map.getFlagList().toArray(new int[flags.length][]);
		for (int i=0; i< state.getFlags().length ; i++){
			boolean oneFlagNotTaken = flags[i];
			//TODO 1st heuristics
			flag = flag && !oneFlagNotTaken;
			//TODO 2nd heuristics
//			flag = flag && ((state.getHealth() && mapFlags[i][0] == state.getX() && mapFlags[i][1] == state.getY() && oneFlagNotTaken) || !oneFlagNotTaken);
		}
		return flag;
	}


	public void expand(){
		T++; //increase number of node expansions
		// get first (minimum) element
		ASearchNode expandidNode = _expansionTree.poll();
		SearchAgentState thisState = expandidNode.getState();
		_out.println("Looking up state: " + thisState);
		if (!thisState.getHealth() || allFlagsTaken(thisState)) _goal = expandidNode;
		SearchAgentState tmpState;
		int tmpX = thisState.getX();
		int tmpY = thisState.getY();
		String tmpD = thisState.getOrientation();
		boolean[] flags = thisState.getFlags();
		boolean health = thisState.getHealth();

		// setting new Sentry position
		int sentryX = thisState.getXSentry();
		int sentryY = thisState.getYSentry();
		String sentryD = thisState.getDSentry();

		//TODO 2nd heuristics
//		List<int[]> flagLocations = _map.getFlagList();
//		flags = Arrays.copyOf(flags, flags.length);
//		for (int i = 0; i< flagLocations.size(); i++){
//			int[] tmpFlag = flagLocations.get(i);
//			if (thisState.getHealth() && tmpFlag[0] == tmpX && tmpFlag[1] == tmpY){
//				flags[i] = false;
//			}
//		}

		if (tmpD == null){ 	//not sliding on ice
			tmpState = getNextState(tmpX-1, tmpY, "NORTH", sentryX, sentryY, sentryD, flags, health);
			if (tmpState!=null) createNode(tmpState, expandidNode, "NORTH");//, _agent.f(tmpState));
			tmpState = getNextState(tmpX+1, tmpY, "SOUTH", sentryX, sentryY, sentryD, flags, health);
			if (tmpState!=null) createNode(tmpState, expandidNode, "SOUTH");
			tmpState = getNextState(tmpX, tmpY-1 , "WEST", sentryX, sentryY, sentryD, flags, health);
			if (tmpState!=null) createNode(tmpState, expandidNode, "WEST");
			tmpState = getNextState(tmpX, tmpY+1 , "EAST", sentryX, sentryY, sentryD, flags, health);
			if (tmpState!=null) createNode(tmpState, expandidNode, "EAST");
		} else {
			if (tmpD.equalsIgnoreCase("North")){
				tmpState = getNextState(tmpX-1, tmpY, "NORTH", sentryX, sentryY, sentryD, flags, health);
				if (tmpState!=null) createNode(tmpState, expandidNode, "NORTH");
			} else if (tmpD.equalsIgnoreCase("East")){
				tmpState = getNextState(tmpX, tmpY+1 , "EAST", sentryX, sentryY, sentryD, flags, health);
				if (tmpState!=null) createNode(tmpState, expandidNode, "EAST");
			} else if (tmpD.equalsIgnoreCase("South")){
				tmpState = getNextState(tmpX+1, tmpY, "SOUTH", sentryX, sentryY, sentryD, flags, health);
				if (tmpState!=null) createNode(tmpState, expandidNode, "SOUTH");
			} else if (tmpD.equalsIgnoreCase("West")){
				tmpState = getNextState(tmpX, tmpY-1 , "WEST", sentryX, sentryY, sentryD, flags, health);
				if (tmpState!=null) createNode(tmpState, expandidNode, "WEST");
			} else {
				tmpState = null;
				System.out.println("ERROR");
			}

		}
	}

	protected SearchAgentState getNextState(int selectedX,int selectedY, String direction, int sentryX, int sentryY, String sentryDirection, boolean[] flags, boolean healthy){
		// status of selected cell
		int cellStat = _map.brickStatus(selectedX, selectedY);
		//stay in place
		if (cellStat == WorldMap.WALL || (selectedX == sentryX && selectedY == sentryY)){
			if (direction.equalsIgnoreCase("North")){
				selectedX++;
			} else if (direction.equalsIgnoreCase("East")){
				selectedY--;
			} else if (direction.equalsIgnoreCase("South")){
				selectedX--;
			} else if (direction.equalsIgnoreCase("West")){
				selectedY++;
			}
		}

		cellStat = _map.brickStatus(selectedX, selectedY); // updated X,Y
		boolean health = !Automaton.isKilling(sentryX, sentryY, selectedX, selectedY, _map) && healthy;

		SentryState nextSentryState = new SentryState(sentryX, sentryY, sentryDirection, _map);
		sentryX = nextSentryState.getX();
		sentryY = nextSentryState.getY();
		sentryDirection = nextSentryState.getDirection();

		if (cellStat == WorldMap.EMPTY){
			return new SearchAgentState(selectedX, selectedY, null, sentryX, sentryY, sentryDirection, health, flags);
		} else if (cellStat == WorldMap.FLAG) {
			//TODO for 1st heuristic
			List<int[]> flagLocations = _map.getFlagList();
			flags = Arrays.copyOf(flags, flags.length);
			for (int i = 0; i< flagLocations.size(); i++){
				int[] tmpFlag = flagLocations.get(i);
				if (healthy && tmpFlag[0] == selectedX && tmpFlag[1] == selectedY){
					flags[i] = false;
				}
			}
			return new SearchAgentState(selectedX, selectedY, null, sentryX, sentryY, sentryDirection, health, flags);
		} else if (cellStat == WorldMap.ICE){
			if (direction.equalsIgnoreCase("North")){
				if(_map.brickStatus(selectedX - 1, selectedY) == WorldMap.WALL){
					return new SearchAgentState(selectedX, selectedY, null,sentryX,sentryY,sentryDirection,health, flags);
				}
			} else if (direction.equalsIgnoreCase("East")){
				if(_map.brickStatus(selectedX, selectedY + 1) == WorldMap.WALL){
					return new SearchAgentState(selectedX, selectedY, null,sentryX,sentryY,sentryDirection,health, flags);
				}
			} else if (direction.equalsIgnoreCase("South")){
				if(_map.brickStatus(selectedX + 1, selectedY) == WorldMap.WALL){
					return new SearchAgentState(selectedX, selectedY, null,sentryX,sentryY,sentryDirection,health, flags);
				}
			} else if (direction.equalsIgnoreCase("West")){
				if(_map.brickStatus(selectedX, selectedY - 1) == WorldMap.WALL){
					return new SearchAgentState(selectedX, selectedY, null,sentryX,sentryY,sentryDirection,health, flags);
				}
			} else {
				return new SearchAgentState(selectedX, selectedY, null,sentryX,sentryY,sentryDirection,health, flags);
			}
			return new SearchAgentState(selectedX, selectedY, direction,sentryX,sentryY,sentryDirection,health, flags);
//			} else
//			health = !Automaton.isKilling(sentryX, sentryY, selectedX, selectedY, _map);
//			return new SearchAgentState(selectedX, selectedY, null,sentryX,sentryY,sentryDirection,health);
		} else {
			System.out.println("###################################");
			System.out.println("cellstat = " + cellStat);
			System.out.println("###################################");
			return null;
		}
	}

	public int getT() {
		return T;
	}
}
